//------------------------------------------------------------
//  Filename: eth_mac_afifo.sv
//  
//  Author  : wlduan@gmail.com 
//  Revise  : 2020-12-01 15:19
//  Description: 
//   
//  All Rights Reserved.                                       
//-------------------------------------------------------------
//
`timescale 1ns/1ps
 
module eth_mac_afifo #(
    parameter FIFO_WIDTH  = 8,
    parameter FIFO_DEEPTH = 8,
    parameter FIFO_VLD_OP = 1
)( 
    input  logic                 rstn_i,  
      
    input  logic                 wclk,  
    input  logic[FIFO_WIDTH-1:0] wdata,
    input  logic                 wvalid, 
    output logic                 wready, 

    input  logic                 rclk,  
    output logic[FIFO_WIDTH-1:0] rdata,
    output logic                 rvalid,
    input  logic                 rready
);    
//--------------------------------------------------------
localparam PTR_WIDTH = $clog2(FIFO_DEEPTH);
//--------------------------------------------------------
logic[PTR_WIDTH:0] wraddr, wraddr_gray;
logic[PTR_WIDTH:0] rdaddr, rdaddr_gray;
logic[PTR_WIDTH:0] wraddr_rff1, wraddr_rff, wraddr_wff;
logic[PTR_WIDTH:0] rdaddr_wff1, rdaddr_wff, rdaddr_rff;
//--------------------------------------------------------
logic[FIFO_WIDTH-1:0] data_buf[FIFO_DEEPTH-1:0];
logic[FIFO_WIDTH-1:0] rdata_q;
//--------------------------------------------------------
always_comb begin
    wraddr = wraddr_wff;
    if(wready&wvalid) wraddr = wraddr_wff + 1;
end
//--------------------------------------------------------
always_ff @(posedge wclk , negedge rstn_i) begin
    if(!rstn_i)
        wraddr_wff <= 0;
    else if(wready&wvalid)
        wraddr_wff <= wraddr;
end
//---- Convert wraddr to Gray code ----//
assign wraddr_gray = (wraddr_wff >> 1) ^ wraddr_wff;
//----- Clock sync to rclk --------//
always_ff @(posedge rclk , negedge rstn_i) begin
    if(!rstn_i) begin
        wraddr_rff  <= 0;
        wraddr_rff1 <= 0;
    end
    else begin
        wraddr_rff  <= wraddr_gray;
        wraddr_rff1 <= wraddr_rff;
    end
end
//--------------------------------------------------------
always_comb begin
    rdaddr = rdaddr_rff;
    if(rvalid&rready) rdaddr = rdaddr_rff + 1;
end
//--------------------------------------------------------
always_ff @(posedge rclk , negedge rstn_i) begin
    if(!rstn_i)
        rdaddr_rff <= 0;
    else if(rvalid&rready)
        rdaddr_rff <= rdaddr;
end
//------ Convert rdaddr to Gray code -------//
assign rdaddr_gray = (rdaddr_rff >> 1) ^ rdaddr_rff;
//------- Clock Sync to wclk ----------//
always_ff @(posedge wclk , negedge rstn_i) begin
    if(!rstn_i) begin
        rdaddr_wff  <= 0;
        rdaddr_wff1 <= 0;
    end
    else begin
        rdaddr_wff  <= rdaddr_gray;
        rdaddr_wff1 <= rdaddr_wff;
    end
end
//--------------------------------------------------------
genvar i;
generate 
    for (i = 0;i < FIFO_DEEPTH; i ++) begin
        always_ff @(posedge wclk , negedge rstn_i) begin
            if(!rstn_i) begin
                data_buf[i] <= 'b0;
            end
            else if((wvalid&wready)&&(wraddr_wff[PTR_WIDTH-1:0] == i)) begin
                data_buf[i] <= wdata;
            end
        end
    end
endgenerate
//--------------------------------------------------------
always_comb begin
    rdata = rdata_q;
    if(rvalid&rready) rdata = data_buf[rdaddr_rff[PTR_WIDTH-1:0]];
end
//------- Clock Sync to rclk ----------//
always_ff @(posedge rclk , negedge rstn_i) begin
    if(!rstn_i) begin
        rdata_q <= 0;
    end
    else begin
        rdata_q <= rdata;
    end
end
//------ Full and Empty Conditions -----------------//
logic  full,empty;
//--------------------------------------------------------
logic[PTR_WIDTH-1:0]  w_count;
logic[PTR_WIDTH  :0]  r_wptr_f;
logic[PTR_WIDTH-1:0]  r_wptr;

if(FIFO_VLD_OP == 0) begin
    function logic[PTR_WIDTH:0] gray2bin(input logic[PTR_WIDTH:0] gray_in);
        logic[7:0] i,j;
        logic[PTR_WIDTH:0] bin_code;
        for(i=0;i<=PTR_WIDTH;i++)  begin
            bin_code[i]=gray_in[i];
            for(j=i;j<PTR_WIDTH;j++) bin_code[i]=bin_code[i]^gray_in[j+1];
        end
        gray2bin=bin_code;
    endfunction

    assign r_wptr_f = gray2bin(rdaddr_wff1);
    assign r_wptr   = r_wptr_f[PTR_WIDTH-1:0];
    assign w_count  = (wraddr_gray[PTR_WIDTH] != rdaddr_wff1[PTR_WIDTH])?(wraddr_wff + FIFO_DEEPTH - r_wptr):(wraddr_wff - r_wptr);
    assign full     = (w_count >= (FIFO_DEEPTH/2));
end
else begin
    assign full  = (wraddr_gray[PTR_WIDTH]   != rdaddr_wff1[PTR_WIDTH]  )&&(wraddr_gray[PTR_WIDTH-1:0] == rdaddr_wff1[PTR_WIDTH-1:0]);
end
//--------------------------------------------------------
assign empty = (wraddr_rff1[PTR_WIDTH:0] == rdaddr_gray[PTR_WIDTH:0]);
//--------------------------------------------------------
assign wready= (~full);
assign rvalid= (~empty);

endmodule
